﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using Mbs;
using Mbs.ComponentModel;
using Mbs.Windows;

namespace ModuleManager.Module
{
   
    [Serializable]
    [DebuggerDisplay("Title={Title},Property={Property}")]
    public class ColumnDescriptor:IComparable<ColumnDescriptor>
    {
        public string Title;
        public string Property;
        public Type Type;
        public int Width;
        public string Format;
        public IFormatProvider FormatProvider;
        //public bool FilterColumn;
       
        public int CompareTo(ColumnDescriptor other)
        {
            return this.Title.CompareTo(other.Title);
        }
    }

    public class GridControl : UserControlView
    {
        private DataGridView _dataGrid = new DataGridView();

        public GridControl()
        {
            Size = new System.Drawing.Size(677, 567);
            _dataGrid.Anchor = 
                AnchorStyles.Top
                | AnchorStyles.Bottom
                | AnchorStyles.Left
                | AnchorStyles.Right;

            Controls.Add(_dataGrid );
        }
      
       
        public DataGridView GridView
        {
            get { return _dataGrid; }
        }

       
    }

    public class GridControl<T> : GridControl where T : class
    {
        private DataTable _dataTable = new DataTable();
        private BindingSource _bindingSource = new BindingSource();
        protected Dictionary<int, ColumnDescriptorWrapper> _colDescriptors = new Dictionary<int, ColumnDescriptorWrapper>();

        protected DataTable DataTable
        {
            get { return _dataTable; }
        }

        protected BindingSource BindingSource
        {
            get { return _bindingSource; }
        }

        protected ColumnDescriptor[] Columns
        {
            get
            {
                return (from item in _colDescriptors
                       select item.Value.ColumnDescriptor).ToArray();
            }
        }
        protected class ColumnDescriptorWrapper
        {
            public ColumnDescriptor ColumnDescriptor;
            public MemberInfo MemberInfo;
            internal DataColumn Column;
            

            private const BindingFlags memberFlags = BindingFlags.GetField | BindingFlags.GetProperty;
            internal void PopulateMemberInfo(Type type)
            {
                MemberInfo[] ms = type.GetMember(ColumnDescriptor.Property);
                if (ms != null && ms.Length == 1)
                    MemberInfo = ms[0];
            }

            internal object GetValue(object o)
            {
                if (MemberInfo == null)
                    return DBNull.Value;

                PropertyInfo p = MemberInfo as PropertyInfo;

                try
                {
                    if (p != null)
                    {
                        object obj = p.GetValue(o, null);
                        if (obj != null)
                        {
                            var enumObj = obj as Enum;
                            if (enumObj != null)
                                return enumObj.GetDescription();
                            return obj;
                        }
                        else
                            return DBNull.Value;
                    }

                    FieldInfo f = MemberInfo as FieldInfo;
                    if (f != null)
                        return f.GetValue(o);
                    else
                        return null;
                }
                catch (Exception ex)
                {
                    return DBNull.Value;
                }
            }

            internal string GetTitle()
            {
                if (MemberInfo == null)
                    return StringParser.Parse(ColumnDescriptor.Title);

                var attr = Attribute.GetCustomAttribute(MemberInfo, typeof(LocalizedPropertyAttribute)) as LocalizedPropertyAttribute;
                if (attr != null && !string.IsNullOrEmpty(attr.Name))
                    return attr.Name.StringResource();

                return StringParser.Parse(ColumnDescriptor.Title);
            }

        }

        public void AddColumns(params ColumnDescriptor[] descriptor)
        {
            GridView.SuspendLayout();
            Type type = typeof(T);

            _dataTable.Columns.Add(new DataColumn("HiddenTag", type));

            foreach (var c in descriptor)
            {
                var item = new ColumnDescriptorWrapper { ColumnDescriptor = c };
                item.PopulateMemberInfo(type);
                item.Column = _dataTable.Columns.Add(item.GetTitle(), c.Type);
                _colDescriptors.Add(item.Column.Ordinal, item);
            }

            _bindingSource.DataSource = _dataTable;
            GridView.DataSource = _bindingSource;
            GridView.ResumeLayout();
        }

        public string Filter
        {
            get
            {
                return _bindingSource.Filter;
            }
            set
            {
                if (value != null)
                {
                    _bindingSource.Filter = value;
                }
            }
        }

        public string SortColumns
        {
            get
            {
                return _bindingSource.Sort;
            }
            set
            {
                if (value != null)
                {
                    _bindingSource.Sort = value;
                }
            }
        }

        public void ClearColumns()
        {
            _colDescriptors.Clear();
        }

        protected DataGridViewColumn GetGridColumn(ColumnDescriptor col)
        {
            foreach (DataGridViewColumn item in GridView.Columns)
            {
                if (item.Tag == col)
                    return item;
            }
            return null;
        }

        public void RefreshColumns(ColumnDescriptor[] cols)
        {
            foreach (var cd in _colDescriptors.Values)
            {
                var col = GetGridColumn(cd.ColumnDescriptor);
                col.Name = cd.ColumnDescriptor.Title;

                if (cols.Contains(cd.ColumnDescriptor))
                {
                    col.Visible = true;
                    col.DisplayIndex =Array.IndexOf<ColumnDescriptor>( cols,cd.ColumnDescriptor);
                }
                else
                {
                    GridView.Columns[cd.ColumnDescriptor.Title].Visible = false;
                }
            }
        }

        public override void RefreshResource()
        {
            foreach (var item in _colDescriptors)
            {
                var col = GetGridColumn(item.Value.ColumnDescriptor);
                if (col != null)
                    col.HeaderText = item.Value.GetTitle();
            }

            foreach (DataRow row in _dataTable.Rows)
            {
                var item = row[0] as T;
                row.BeginEdit();
                FillRow(item, row);
                row.EndEdit();
            }
        }

        public T[] SelectedItems
        {
            get
            {
                var selections = GridView.SelectedRows;
                var selectObjects = new T[selections.Count];
                var i = 0;
                foreach (DataGridViewRow row in selections)
                {
                    var drv = row.DataBoundItem as DataRowView;

                    if (drv != null)
                    {
                        T obj = drv.Row.ItemArray[0] as T;

                        if (obj != null)
                            selectObjects[i++] = obj;
                    }
                }

                return selectObjects;
            }
        }

        public T Find(Predicate<T> filter)
        {
            if (filter == null)
                return default(T);

            foreach (DataRow row in DataTable.Rows)
            {
                var item = row[0] as T;
                if (item != null)
                    if (filter(item))
                        return item;
            }

            return default(T);
        }

        public T[] FindAll(Predicate<T> filter)
        {
            if (filter == null)
                return new T[0];

            var rs = new List<T>();
            foreach (DataRow row in DataTable.Rows)
            {
                var item = row[0] as T;
                if (item != null)
                    if (filter(item))
                        rs.Add(item);
            }

            return rs.ToArray();
        }

        public T CurrentItem
        {
            get
            {
                var currentRow = _bindingSource.Current as DataRowView;
                if (currentRow != null)
                    return currentRow.Row[0] as T;
                return null;
            }
            set
            {
                if (value == null)
                    return;

                var length = _bindingSource.Count;

                for(int i= 0; i < length; i++)
                {
                    var rv = _bindingSource[i] as DataRowView;
                    if (rv != null)
                        if (rv.Row[0] == value)
                            _bindingSource.Position = i;
                }
            }
        }

        public GridControl()
        {
            //GridView.CellFormatting += new DataGridViewCellFormattingEventHandler(_dataGrid_CellFormatting);
            GridView.ColumnAdded += new DataGridViewColumnEventHandler(_dataGrid_ColumnAdded);
        }

        //private void _dataGrid_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
        //{
        //    if (e == null)
        //        return;

        //    int column = e.ColumnIndex;

        //    if (e.RowIndex >= 0 && e.RowIndex <= GridView.Rows.Count - 1)
        //        OnCellFormating(e);
        //}

        //protected virtual void OnCellFormating(DataGridViewCellFormattingEventArgs e)
        //{
        //    var currentObj = this[e.RowIndex];

        //    if (currentObj != null)
        //    {
        //        var style = GetRowStyle(currentObj);

        //        if (style != null)
        //        {
        //            //Get the row
        //            var row = GridView.Rows[e.RowIndex];
        //            row.DefaultCellStyle = style;
        //        }
        //    }
        //}


        protected virtual DataGridViewCellStyle GetRowStyle(T row)
        {
            return null;
        }

        #region IListDisplayer<TTarget> Members

        public delegate void DisplayerDelegate(T[] add);

        protected virtual void BeforeAdded(T[] add)
        {

        }

        protected virtual void AfterAdded(T[] add)
        {

        }

        public void Add(T[] add)
        {
            if (InvokeRequired)
                BeginInvoke(new DisplayerDelegate(Add), new object[] { add });
            else
            {
                BeforeAdded(add);
                foreach (T o in add)
                    _dataTable.Rows.Add(NewRow(o));
                AfterAdded(add);
            }
        }

       
        public virtual int Compare(T obj1, T obj2)
        {
            if (obj1 is IComparable)
            {
                return ((IComparable)obj1).CompareTo(obj2);
            }
            else
            {
                throw new Exception("Object does not implement IComparable");
            }
        }

        public void Remove(T[] remove)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new DisplayerDelegate(Remove), new object[] { remove });
            }
            else
            {
                var removeRows = new List<DataRow>();

                foreach (var obj in remove)
                    foreach (DataRow dr in _dataTable.Rows)
                        if (Compare(obj, dr["HiddenTag"] as T) == 0)
                            removeRows.Add(dr);

                foreach (DataRow row in removeRows)
                    _dataTable.Rows.Remove(row);
            }
        }

        public void Update(T[] update)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new DisplayerDelegate(Update), new object[] { update });
            }
            else
            {
                Remove(update);
                Add(update);
            }
        }

        public virtual void Populate(T[] items)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new DisplayerDelegate(Populate), new object[] { items });
            }
            else
            {
                _bindingSource.RaiseListChangedEvents = false;
                GridView.SuspendLayout();

                Clear();

                if (null != items && items.Length > 0)
                    Add(items);

                GridView.ResumeLayout();

                _bindingSource.RaiseListChangedEvents = true;
                _bindingSource.ResetBindings(false);
            }
        }

        void _dataGrid_ColumnAdded(object sender, DataGridViewColumnEventArgs e)
        {
            OnColumnAdded(e);
        }

        protected virtual void OnColumnAdded(DataGridViewColumnEventArgs e)
        {
            if (e.Column.Index == 0
                || 
                !_colDescriptors.ContainsKey(e.Column.Index))
            {
                e.Column.Visible = false;
            }
            else
            {
                var cd = _colDescriptors[e.Column.Index];
                e.Column.Name = cd.ColumnDescriptor.Title;
                e.Column.Tag = cd.ColumnDescriptor;

                if (cd.ColumnDescriptor.FormatProvider != null)
                {
                    e.Column.DefaultCellStyle.FormatProvider = cd.ColumnDescriptor.FormatProvider;

                    if (cd.ColumnDescriptor.Format == null)
                        e.Column.DefaultCellStyle.Format = ".";
                }

                if (cd.ColumnDescriptor.Format != null)
                    e.Column.DefaultCellStyle.Format = cd.ColumnDescriptor.Format;
            }
        }

        public T this[int position]
        {
            get
            {
                if (_bindingSource.Count >= 0
                    && position <= _bindingSource.Count - 1)
                {
                    var drv = _bindingSource[position] as DataRowView;

                    if (drv != null && drv[0] != null)
                        return drv[0] as T;
                }

                return null;
            }
        }

        protected virtual DataRow NewRow(T o)
        {
            var dr = _dataTable.NewRow();

            dr[0] = o;

            FillRow(o, dr);

            return dr;
        }

        private void FillRow(T o, DataRow dr)
        {
            foreach (var colDesc in _colDescriptors.Values)
            {
                if (colDesc.MemberInfo == null)
                    continue;
                dr[colDesc.Column.ColumnName] = colDesc.GetValue(o);
            }
        }

        public void Clear()
        {
            _dataTable.Rows.Clear();
        }

        #endregion

    }
}
